package org.apache.commons.graph.model

import junit.framework.Assert
import org.apache.commons.graph.CommonsGraph.newDirectedMutableGraph
import org.apache.commons.graph.CommonsGraph.newUndirectedMutableGraph
import org.apache.commons.graph.CommonsGraph.populate
import org.apache.commons.graph.Graph
import org.apache.commons.graph.MutableGraph
import org.apache.commons.graph.builder.AbstractGraphConnection
import org.apache.commons.graph.builder.GraphConnection
import org.apache.commons.graph.weight.primitive.DoubleWeightBaseOperations
import org.junit.After
import org.junit.Test
import java.io.*

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/

/**
 * Provides a simple test case to test the Graph serialization.
 */
class GraphSerializationTestCase {
    @After
    fun cleanUp() {
        val f = File(FILE_NAME)
        if (f.exists()) {
            f.delete()
        }
    }

    @Test
    @Throws(Exception::class)
    fun testSerializeDirectedGraph() {
        val g: MutableGraph<BaseLabeledVertex, BaseLabeledEdge> = newDirectedMutableGraph(buildGraphConnections())

        checkSerialization(g)
    }

    @Test
    @Throws(Exception::class)
    fun testSerializeDirectedWeightdGraph() {
        val g: MutableGraph<BaseLabeledVertex, BaseLabeledWeightedEdge<Double>> =
            newDirectedMutableGraph(buildWeightedGraphConnections())

        checkSerialization(g)
    }

    @Test
    @Throws(Exception::class)
    fun testSerializePath() {
        val start = BaseLabeledVertex("start")
        val goal = BaseLabeledVertex("goal")
        val a = BaseLabeledVertex("a")
        val b = BaseLabeledVertex("b")
        val c = BaseLabeledVertex("c")

        val g = InMemoryWeightedPath<BaseLabeledVertex, BaseLabeledWeightedEdge<Double>, Double>(
                start,
                goal,
                DoubleWeightBaseOperations,
                BaseWeightedEdge()
            )

        g.addConnectionInTail(start, BaseLabeledWeightedEdge("start <-> a", 1.5), a)
        g.addConnectionInTail(a, BaseLabeledWeightedEdge("a <-> b", 2.0), b)
        g.addConnectionInTail(b, BaseLabeledWeightedEdge("b <-> c", 3.0), c)
        g.addConnectionInTail(c, BaseLabeledWeightedEdge("c <-> goal", 3.0), goal)

        checkSerialization(g)
    }

    @Test
    @Throws(Exception::class)
    fun testSerializeSpanningTree() {
        val spanningTree = MutableSpanningTree<BaseLabeledVertex, BaseLabeledWeightedEdge<Double>, Double>(
                DoubleWeightBaseOperations,
                BaseWeightedEdge()
            )

        populate(spanningTree).withConnections(buildWeightedGraphConnections())

        checkSerialization(spanningTree)
    }

//    @Test
//    @Throws(Exception::class)
//    fun testSerializeSyncronyzedDirectedWeightdGraph() {
//        val g: Graph<BaseLabeledVertex, BaseLabeledWeightedEdge<Double>?> =
//            synchronize(newDirectedMutableGraph(buildWeightedGraphConnections()) as MutableGraph<BaseLabeledVertex?, BaseLabeledWeightedEdge<Double?>?>?)
//
//        checkSerialization(g)
//    }

    @Test
    @Throws(Exception::class)
    fun testSerializeUndirectedGraph() {
        val g: MutableGraph<BaseLabeledVertex, BaseLabeledEdge> = newUndirectedMutableGraph(buildGraphConnections())

        checkSerialization(g)
    }

    @Test
    @Throws(Exception::class)
    fun testSerializeUndirectedWeightdGraph() {
        val g: MutableGraph<BaseLabeledVertex, BaseLabeledWeightedEdge<Double>> =
            newUndirectedMutableGraph(buildWeightedGraphConnections())

        checkSerialization(g)
    }

    companion object {
        private const val FILE_NAME = "target/serialiazedGraph.dat"

        private fun buildGraphConnections(): GraphConnection<BaseLabeledVertex, BaseLabeledEdge> {
            return object : AbstractGraphConnection<BaseLabeledVertex, BaseLabeledEdge>() {
                override fun connect() {
                    val a = addVertex(BaseLabeledVertex("a"))
                    val b = addVertex(BaseLabeledVertex("b"))
                    val c = addVertex(BaseLabeledVertex("c"))
                    val d = addVertex(BaseLabeledVertex("d"))

                    addEdge(BaseLabeledEdge("a -> c")).from(a).to(c)
                    addEdge(BaseLabeledEdge("c -> d")).from(c).to(d)
                    addEdge(BaseLabeledEdge("d -> b")).from(d).to(b)
                }
            }
        }

        private fun buildWeightedGraphConnections(): GraphConnection<BaseLabeledVertex, BaseLabeledWeightedEdge<Double>> {
            return object : AbstractGraphConnection<BaseLabeledVertex, BaseLabeledWeightedEdge<Double>>() {
                override fun connect() {
                    val a = addVertex(BaseLabeledVertex("a"))
                    val b = addVertex(BaseLabeledVertex("b"))
                    val c = addVertex(BaseLabeledVertex("c"))
                    val d = addVertex(BaseLabeledVertex("d"))

                    addEdge(BaseLabeledWeightedEdge("a -> c", 1.0)).from(a).to(c)
                    addEdge(BaseLabeledWeightedEdge("c -> d", 1.0)).from(c).to(d)
                    addEdge(BaseLabeledWeightedEdge("d -> b", 1.0)).from(d).to(b)
                }
            }
        }

        /**
         * @param g
         * @throws FileNotFoundException
         * @throws IOException
         * @throws ClassNotFoundException
         */
        @Throws(FileNotFoundException::class, IOException::class, ClassNotFoundException::class)
        private fun checkSerialization(g: Graph<BaseLabeledVertex, out Serializable?>) {
            val fout = FileOutputStream(FILE_NAME)
            val oos = ObjectOutputStream(fout)
            oos.writeObject(g)
            oos.close()

            val fin = FileInputStream(FILE_NAME)
            val ois = ObjectInputStream(fin)
            val cloned = ois.readObject()
            ois.close()

            Assert.assertEquals(g, cloned)
        }
    }
}